
import itsdangerous
from flask import request, current_app
from flask_login import UserMixin, current_user

from sqlalchemy import String, Column, Integer, DateTime, Enum, Text
from sqlalchemy import func, ForeignKey
from sqlalchemy.orm import relationship

from app import bcrypt, db, login_manager

# from werkzeug.security import generate_password_hash, check_password_hash

# flask_login 回调函数 返回user对象
@login_manager.user_loader
def load_user(uid):
    return User.query.filter_by(id=uid, deleted="false").first()


# 基类
class Model(db.Model):
    __abstract__ = True

    id = Column(Integer, primary_key=True, autoincrement=True)
    created_time = Column(DateTime, server_default=func.current_timestamp())
    updated_time = Column(DateTime, server_default=func.current_timestamp())
    # 软删除标记 在开发中较少的使用直接删除数据，一般使用软删除。
    deleted = Column(Enum("true", "false"), nullable=False,
                    server_default="false")

# 粉丝关注 关联表 自关联多对多
class Follows(db.Model):
    __tablename__ = "follows"
    created_time = Column(DateTime, server_default=func.current_timestamp())
    updated_time = Column(DateTime, server_default=func.current_timestamp())
    deleted = Column(Enum("true", "false"), nullable=False,
                    server_default="false")
    follower_id = Column(Integer, ForeignKey("user.id"),
                        nullable=False, primary_key=True)
    followed_id = Column(Integer, ForeignKey("user.id"),
                        nullable=False, primary_key=True)

# 用户表
class User(Model, UserMixin):
    __tablename__ = "user"  # 表名称

    # 字段
    username = Column(String(64), nullable=False, unique=True)
    password_hash = Column(String(128), nullable=False)
    email = Column(String(64), nullable=False, unique=True)
    state = Column(Enum("true", "false"),
                    nullable=False, server_default="true")
    intro = Column(String(128), default="这家伙很可爱，所以什么都没写~")
    avatar_img_url = Column(
        String(128), default="/static/imgs/default/default_avatar.jpg")
    ip = Column(String(32))
    role_id = Column(Integer, ForeignKey("role.id"), nullable=False)  # 外键

    # flask_sqlalchemy 反向查询字段；不会再数据库中添加字段，重视sqlalchemy为了方便查询添加的
    posts = relationship('Post', backref='author', lazy='dynamic')
    articles = relationship('Article', backref='author', lazy='dynamic')
    followed = relationship("User", secondary="follows",
                            primaryjoin="follows.c.follower_id == User.id",
                            secondaryjoin="follows.c.followed_id == User.id",
                            backref=db.backref("followers", lazy="dynamic"), lazy="dynamic"
                        )

    def __init__(self, **kwargs) -> None:
        super(User, self).__init__(**kwargs)
        # 设定默认角色
        if self.role is None:
            self.role = Role.query.filter_by(default="true").first()
        
        # 获取用户ip， 感觉没什么用。。
        if not self.ip:
            self.ip = request.remote_addr


    #  这个”属性“ 代替password_hash 对外使用
    @property
    def password(self):
        raise AttributeError('哎呀呀，宝友，这可不兴读啊！！')  # 禁止读取

    @password.setter
    def password(self, new_pwd):
        self.password_hash = bcrypt.generate_password_hash(new_pwd) # 密码加密

    def verify_password(self, pwd):
        return bcrypt.check_password_hash(self.password_hash, pwd) # 校验密码 返回 True or False

    # 生成 重置密码的 token
    def generate_token(self, t=0):
        if t == 0:
            t = current_app.config.get("DELAULT_TIMEOUT_EMAIL_CODE", 300)

        s = itsdangerous.TimedJSONWebSignatureSerializer(
            current_app.config["SECRET_KEY"], t)
        return s.dumps({"id": self.id})

    # token 校验    返回 None or User 对象
    @staticmethod
    def check_token(token):
        s = itsdangerous.TimedJSONWebSignatureSerializer(
            current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token)
            return User.query.filter_by(id=data["id"], deleted="false").first()
        except:
            return

    # 判断用户身份
    def is_author_permission(self):
        if (self.role.name != "Author"):
            return False
        return True

    # 判断用户与用户之间有没有关注
    def is_following(self, user):
        return Follows.query.filter(user.id==Follows.follower_id, self.id==Follows.followed_id ).first() is not None

    # 增加粉丝
    def follow(self, user):
        if not self.is_following(user):
            self.followers.append(user)
            db.session.commit()
    
    # 取关操作
    def unfollow(self, user):
        if self.is_following(user):
            self.followers.remove(user)
            db.session.commit()

    # 使用 print 函数时显示的格式
    def __repr__(self):
        return "ID=%d : %s@%s" % (self.id, self.username, self.email)

# 权限表
class Permission(Model):
    __tablename__ = "permission"
    name = Column(String(32))
    intro = Column(String(64), default=name)

    # 已有的权限
    @staticmethod
    def insert_permission():
        permissions = [
            "FOLLOW",  # 关注
            "COMMENT",  # 评论
            "WRITE_ARTICLES",  # 写文
            "MODERATE_COMMENTS",   # 评论审核
            "ADMINISTER",  # 管理员
        ]
        length = len(permissions)
        for n in range(length):
            p = Permission.query.filter_by(name=permissions[n]).first()
            if not p:
                p = Permission(name=permissions[n])
                db.session.add(p)
        db.session.commit()

# 权限 角色表 多对多
class Permission_Role(db.Model):
    __tablename__ = "permission_role"
    created_time = Column(DateTime, server_default=func.current_timestamp())
    updated_time = Column(DateTime, server_default=func.current_timestamp())
    deleted = Column(Enum("true", "false"), nullable=False,
                    server_default="false")
    permission_id = Column(Integer, ForeignKey(
        "permission.id"), nullable=False, primary_key=True)
    role_id = Column(Integer, ForeignKey("role.id"),
                    nullable=False, primary_key=True)

# 用户申请提权表
class User_Role_Permission_Request(Model):
    __tablename__ = "user_role_permission_request"

    title = Column(String(64), nullable=False)
    text = Column(Text, nullable=False)
    user_id = Column(Integer, ForeignKey("user.id"), nullable=False)

# 角色表
class Role(Model):
    __tablename__ = "role"
    name = Column(String(16), nullable=False)
    default = Column(Enum("true", "false"), nullable=False)
    intro = Column(String(128))
    # sqlalchemy 提供的反向查询，并不会在数据库中创建字段
    users = relationship('User', backref='role', lazy='dynamic')
    permissions = relationship(
        "Permission", backref='roles', secondary="permission_role", lazy='dynamic')

    @staticmethod
    def insert_roles():
        follow = Permission.query.filter_by(name="FOLLOW").first()
        comment = Permission.query.filter_by(name="COMMENT").first()
        write_articles = Permission.query.filter_by(
            name="WRITE_ARTICLES").first()
        moderate_comments = Permission.query.filter_by(
            name="MODERATE_COMMENTS").first()
        administer = Permission.query.filter_by(name="ADMINISTER").first()
        roles = {
            "User": {
                "Permissions": [follow, comment],
                "Intro": "普通用户",
                "Default": "true"
            },
            "Author": {
                "Permissions": [follow, comment, write_articles, moderate_comments],
                "Intro": "创作者",
                "Default": "false"
            },
            "Administer": {
                "Permissions": [follow, comment, write_articles, moderate_comments, administer],
                "Intro": "管理员",
                "Default": "false"
            }
        }

        for k in roles:
            role = Role.query.filter_by(name=k).first()
            if not role:
                role = Role(name=k)
                role.intro = roles[k]["Intro"]
                role.permissions = roles[k]["Permissions"]
                role.default = roles[k]["Default"]

                db.session.add(role)
        db.session.commit()

    @staticmethod
    def user_set_permission_author(user):
        user.role = Role.query.filter_by(
            deleted="false", name="Author").first()
        db.session.commit()

    def __repr__(self):
        return "ID=%d : %s" % (self.id, self.name)


# post表 代表动态
class Post(Model):
    __tablename__ = "post"
    body = Column(Text, nullable=False)
    user_id = Column(Integer, ForeignKey("user.id"), nullable=False)

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        if self.author is None:
            self.author = current_user

# 上传的图片记录表
class Images(Model):
    __tablename__ = "image"

    url = Column(String(128), nullable=False)
    user_id = Column(Integer, ForeignKey("user.id"), nullable=False)

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        if not self.user_id:
            self.user_id = current_user.id

# 文章表
class Article(Model):
    __tablename__ = "article"

    title = Column(String(64), nullable=False)
    cover_url = Column(
        String(128), default="/static/imgs/default/default_cover.jpg")
    abstract = Column(String(255), nullable=False)
    content = Column(Text, nullable=False)
    draft = Column(Enum("true", "false"), nullable=False,
                    server_default="false")
    user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
    tags = relationship("Tag", backref='articles',
                        secondary="article_tag", lazy='dynamic')

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        if not self.user_id:
            self.user_id = current_user.id

# 文章 标签 表
class Article_Tag(db.Model):
    __tablename__ = "article_tag"
    created_time = Column(DateTime, server_default=func.current_timestamp())
    updated_time = Column(DateTime, server_default=func.current_timestamp())
    deleted = Column(Enum("true", "false"), nullable=False,
                    server_default="false")
    article_id = Column(Integer, ForeignKey("article.id"),
                        nullable=False, primary_key=True)
    tag_id = Column(Integer, ForeignKey("tag.id"),
                    nullable=False, primary_key=True)

# 标签表
class Tag(Model):
    __tablename__ = "tag"
    name = Column(String(16), nullable=False)
    intro = Column(String(128), default=name)

    @staticmethod
    def insert_tag(ls):
        ts = []
        for t in ls:
            tag = Tag.query.filter_by(name=t, deleted="false").first()
            if tag:
                ts.append(tag)
            else:
                new_tag = Tag(name=t)
                db.session.add(new_tag)
                ts.append(new_tag)

        db.session.commit()
        return ts
